﻿using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using SharedKernel.Infrastructure.Comm;
using SharedKernel.Infrastructure.Caches; 
using ApiGateway.Config;
using System.Net;
using SharedKernel.Infrastructure.Redis;

namespace ApiGateway.Handler
{
    /// <summary>
    /// 统一的Jwt校验
    /// </summary>
    public class JwtAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
    {
        private readonly IUserContext _userContext;
        private readonly IConfiguration _configuration;
        private readonly ICacheService _cacheService;
        private SystemConfig _systemConfig;
        private static Dictionary<string, string> AuthFailDict = new Dictionary<string, string>
        {
            { "10001", "当前未登录" },
            { "10002", "Token格式错误" },
            { "10003", "您的账号在其他地方登录，请重新登录" },
            { "10004", "用户信息丢失，请重新登录" },
            { "10005", "登陆已过期，请重新登录" },
            { "10006", "请勿非法操作" },
            { "10007", "服务器错误" },
            //只针对错误请求计数，正常请求不计数
            { "10008", "请求太频繁，请稍后再试" },
        };

        public JwtAuthenticationHandler(IConfiguration configuration, ICacheService cacheService,
         IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, IOptionsMonitor<SystemConfig> systemConfigMonitor,
         UrlEncoder encoder, ISystemClock clock, IUserContext userContext) : base(options, logger, encoder, clock)
        {
            _userContext=userContext;
            _configuration=configuration;
            _cacheService=cacheService;

            _systemConfig = systemConfigMonitor.CurrentValue;
            systemConfigMonitor.OnChange(newOptions =>
            {
                _systemConfig = newOptions;
            });
        }

        /// <summary>
        /// 鉴权
        /// </summary>
        /// <returns></returns>
        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            var endpoint = Context.GetEndpoint();
            AuthenticationTicket ticket;
            //如果不存在鉴权特性，直接返回一个空的ticket
            if (endpoint?.Metadata.GetMetadata<AuthorizeAttribute>() == null)
            {
                // 模拟用户信息
                ticket = GetTicket(0);
                return AuthenticateResult.Success(ticket);
            }

            var ip = GetClientIp(Context);
            //切换到UserServiceDb数据库
            _cacheService.ChangeDataBase(RedisDbConstConfig.ApiGatewayDb);
            var errCountKey = RedisKeyConstConfig.ApiGatewayErrRequestCount(ip);
            var errCount = await _cacheService.GetAsync<int>(errCountKey);
            if (errCount>=_systemConfig.ErrorRequestCount)
            {
                //错误请求太频繁了
                Context.Items["AuthFailCode"] = "10008";
                return AuthenticateResult.NoResult();
            }

            // 从请求头中获取 Token
            Request.Headers.TryGetValue("Authorization", out var headerToken);
            if (string.IsNullOrWhiteSpace(headerToken))
            {
                Context.Items["AuthFailCode"] = "10001";
                return AuthenticateResult.NoResult();
            }

            var token = headerToken.ToString();
            if (!token.StartsWith("Bearer "))
            {
                Context.Items["AuthFailCode"] = "10002";
                Context.Items["ErrIp"] = ip;
                return AuthenticateResult.NoResult();
            }

            token=token.Replace("Bearer ", string.Empty);

            // 验证Token的参数
            var tokenValidationParameters = new TokenValidationParameters
            {
                //验证发行人
                ValidateIssuer = true,
                ValidIssuer = _configuration["Jwt:Issuer"],
                //验证受众
                ValidateAudience = true,
                ValidAudience = _configuration["Jwt:Audience"],
                // 验证 Token 是否过期
                ValidateLifetime = true,
                RequireExpirationTime = true,
                //验证额外的签名
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"])),
                // 可选：允许时钟偏移（解决服务器时间不同步问题）
                //ClockSkew = TimeSpan.FromMinutes(5)
            };

            var jwtHandler = new JwtSecurityTokenHandler();
            try
            {
                //验证
                var principal = jwtHandler.ValidateToken(token, tokenValidationParameters, out var validatedToken);

                //检查 Token 类型是否为 JWT（防止伪造）
                if (!(validatedToken is JwtSecurityToken jwtToken) || !jwtToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.OrdinalIgnoreCase))
                {
                    Context.Items["AuthFailCode"] = "10006";
                    Context.Items["ErrIp"] = ip;
                    return AuthenticateResult.NoResult();
                }

                //context.Items["UserClaims"] = principal.Claims;
                //context.Items["UserRole"] = principal.FindFirst(ClaimTypes.Role)?.Value;     // 示例：用户角色 
                if (long.TryParse(principal.FindFirst(ClaimTypes.NameIdentifier)?.Value, out long userId))
                {
                    _userContext.UserId =userId;
                }
                else
                {
                    //用户信息丢失
                    Context.Items["AuthFailCode"] = "10002";
                    Context.Items["ErrIp"] = ip;
                    return AuthenticateResult.NoResult();
                }

                //切换到UserServiceDb数据库
                _cacheService.ChangeDataBase(RedisDbConstConfig.UserServiceDb);

                //获取缓存的Token。如果是内存缓存的情况下，UserService存储的数据，在ApiGateway无法读取。这里会为空
                var userTokenKey = RedisKeyConstConfig.UserServiceLoginToken(userId);
                var cacheToken = await _cacheService.GetAsync<string>(userTokenKey);

                int tokenRetryCount = 1;
                while (tokenRetryCount < 3 && string.IsNullOrWhiteSpace(cacheToken))
                {
                    await Task.Delay(100);
                    cacheToken = await _cacheService.GetAsync<string>(userTokenKey);
                    tokenRetryCount++;
                }

                if (string.IsNullOrWhiteSpace(cacheToken))
                {
                    //用户信息丢失
                    Context.Items["AuthFailCode"] = "10004";
                    return AuthenticateResult.NoResult();
                }

                ticket = GetTicket(userId);

                return AuthenticateResult.Success(ticket);
            }
            catch (SecurityTokenExpiredException ex)
            {
                //token过期
                Context.Items["AuthFailCode"] = "10005";
                return AuthenticateResult.NoResult();
            }
            catch (SecurityTokenInvalidSignatureException ex)
            {
                //签名无效
                Context.Items["AuthFailCode"] = "10002";
                Context.Items["ErrIp"] = ip;
                return AuthenticateResult.NoResult();
            }
            catch (SecurityTokenException ex)
            {
                // 其他 Token 错误（如格式错误、无效声明） 
                Context.Items["AuthFailCode"] = "10002";
                Context.Items["ErrIp"] = ip;
                return AuthenticateResult.NoResult();
            }
            catch (Exception ex)
            {
                //未知错误
                Context.Items["AuthFailCode"] = "10007";
                return AuthenticateResult.NoResult();
            }
        }


        /// <summary>
        /// 获取客户端IP地址
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private string GetClientIp(HttpContext context)
        {
            // 处理代理场景（如Nginx反向代理）
            if (context.Request.Headers.TryGetValue("X-Forwarded-For", out var xForwardedFor))
            {
                return xForwardedFor.ToString().Split(',')[0].Trim();
            }

            return context.Connection.RemoteIpAddress?.ToString() ?? "Unknown";
        }

        /// <summary>
        /// 只有验证失败才会进此方法
        /// </summary>
        /// <param name="properties"></param>
        /// <returns></returns>
        protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
        {
            var endpoint = Context.GetEndpoint();
            Context.Items.TryGetValue("ErrIp", out object? errIp);
            Context.Items.TryGetValue("AuthFailCode", out object? code);

            var authFailCode = code.ToString();
            if (!string.IsNullOrWhiteSpace(authFailCode))
            {
                var authFailText = AuthFailDict.GetValueOrDefault(authFailCode);
                if (errIp!=null&&!string.IsNullOrWhiteSpace(errIp.ToString()))
                {
                    var key = RedisKeyConstConfig.ApiGatewayErrRequestCount(errIp.ToString());
                    _cacheService.ChangeDataBase(RedisDbConstConfig.ApiGatewayDb);
                    var count = await _cacheService.GetAsync<int>(key);
                    //限制请求一分钟
                    await _cacheService.SetAsync<int>(key, ++count, TimeSpan.FromMinutes(1));
                }
                await WriteResponseDataAsync(authFailText, authFailCode);
            }
        }

        /// <summary>
        /// 获取验证通过的ticket
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        private AuthenticationTicket GetTicket(long userId)
        {
            var claims = new[] { new Claim(ClaimTypes.NameIdentifier, userId.ToString()) };
            var identity = new ClaimsIdentity(claims, Scheme.Name);
            var principal = new ClaimsPrincipal(identity);
            var ticket = new AuthenticationTicket(principal, Scheme.Name);
            return ticket;
        }

        /// <summary>
        /// 响应数据
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        private async Task WriteResponseDataAsync(string msg, string data)
        {
            Response.StatusCode = 401;
            Response.ContentType = "application/json";
            var response = ResponseInfo<string>.Fail(msg, data);
            var str = JsonSerializer.Serialize(response);
            await Response.WriteAsync(str);
        }

    }

    public static class IpHelper
    {
        /// <summary>
        /// 获取客户端真实IP地址
        /// </summary>
        /// <param name="context">HTTP上下文</param>
        /// <returns>IP地址</returns>
        public static string GetClientIpAddress(HttpContext context)
        {
            if (context == null)
            {
                return string.Empty;
            }

            // 尝试从X-Forwarded-For头获取IP（当应用程序在代理后面时）
            var xffHeader = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
            if (!string.IsNullOrEmpty(xffHeader))
            {
                var ips = xffHeader.Split(',', StringSplitOptions.RemoveEmptyEntries);
                if (ips.Length > 0)
                {
                    return ips[0].Trim();
                }
            }

            // 从Connection获取IP地址
            var remoteIp = context.Connection.RemoteIpAddress;
            if (remoteIp != null)
            {
                // 如果是IPv6地址，尝试获取对应的IPv4地址
                if (remoteIp.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
                {
                    remoteIp = Dns.GetHostEntry(remoteIp).AddressList
                        .FirstOrDefault(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
                }

                return remoteIp?.ToString() ?? string.Empty;
            }

            return string.Empty;
        }
    }

}
